package ec.edu.uce.tour.dao.crud.impl;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.validation.ConstraintViolationException;

import ec.edu.uce.tour.dao.crud.AccesoDatos;
import ec.edu.uce.tour.dao.util.ParametroSentencia;
import ec.edu.uce.tour.excepcion.AccesoDatosExcepcion;
import ec.edu.uce.tour.excepcion.UceTourException;

/**
 * 
 * @author 
 * Clase generica que permite el acceso a datos abstrayendo al negocio
 * @param <T> entidad de la cual se realizara su Dao
 */

public class AccesoDatosBean<T> implements AccesoDatos<T>{

	@PersistenceContext(unitName="UceTourEJB")
	protected EntityManager em;
	

	
	/**
	 * Metodo que realizara la operacion de persistir el objeto
	 * @param entity entidad que se persistira
	 * @throws AccesoDatosExcepcion
	 */
	@Override
	public void guardar(T entity) throws AccesoDatosExcepcion{
		try{
			em.clear();
			em.persist(entity);
			em.flush();
		}
		catch (Exception e) {
			throw new AccesoDatosExcepcion(e);
		}
	}

	/**
	 * Metodo que sincronizara una entidad en el EntityManager
	 * @param entity
	 * @throws AccesoDatosExcepcion
	 */
	@Override
	public void actualizar(T entity) throws AccesoDatosExcepcion{
		try{
			em.clear();
			em.merge(entity);
			em.flush();
		}catch(ConstraintViolationException e)
		{
			throw new AccesoDatosExcepcion("No se puede ingresar un registro duplicado.", e.getCause());
		
		}catch(PersistenceException e)
		{
			throw new AccesoDatosExcepcion("No se puede ingresar un registro duplicado.", e.getCause());
		}
		catch (Exception e) {
			e.printStackTrace();
			throw new AccesoDatosExcepcion("Error al sincronizar entidad", e.getCause());
			
		}
	}
	
	/**
	 * Metodo que elimina una entidad
	 * @param entity
	 * @throws AccesoDatosExcepcion
	 */
	@Override
	public void eliminar(T entity) throws AccesoDatosExcepcion{
		try{
			em.clear();
			em.remove(entity);
			em.flush();
		}catch(PersistenceException e)
		{
			throw new AccesoDatosExcepcion("No se puede ingresar un registro duplicado.", e.getCause());
		}catch (Exception e) 
		{
			throw new AccesoDatosExcepcion("Error al remover", e.getCause());
		}
	}
	
	/**
	 * Metodo que devolvera un objeto segun su clave primaria
	 * @param entity
	 * @param id
	 * @return T
	 * @throws AccesoDatosExcepcion
	 */
	@Override
	public T buscarPorId(Class<T> entity, Serializable id) throws AccesoDatosExcepcion{
		try{
			return em.find(entity, id);
		}catch (Exception e) {
			throw new AccesoDatosExcepcion("Error al recuperar Entidad", e.getCause());
		}
	}
	
	
	@SuppressWarnings("unchecked")
	@Override
	public List<T> buscarTodo(Class<T> entity) throws AccesoDatosExcepcion
	{
		try{
			Query query = em.createQuery(" from "+ entity.getSimpleName());
			return query.getResultList();
		}catch (Exception e) {
			throw new AccesoDatosExcepcion("Error al recuperar Entidad", e.getCause());
		}
	}
	
	@Override
	public Collection<T> buscarPorQuery(String queryAnonymous) throws AccesoDatosExcepcion {
		return this.buscarPorQuery(queryAnonymous, Boolean.FALSE, null, new ParametroSentencia[0]);
	}

	@Override
	public Collection<T> buscarPorQuery(String queryAnonymous, Boolean isNamed)	throws AccesoDatosExcepcion {
		return this.buscarPorQuery(queryAnonymous, isNamed, null, new ParametroSentencia[0]);
	}

	@Override
	public Collection<T> buscarPorQuery(String queryAnonymous, Boolean isNamed, Integer numRows) throws AccesoDatosExcepcion {
		return this.buscarPorQuery(queryAnonymous, isNamed, numRows, new ParametroSentencia[0]);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Collection<T> buscarPorQuery(String queryAnonymous, Boolean isNamed,	Integer numRows, ParametroSentencia... parameterQueries) throws AccesoDatosExcepcion {
		Query query;
		if(isNamed){
			query = em.createNamedQuery(queryAnonymous);
		}else{
			query = em.createQuery(queryAnonymous);
		}
		if(numRows != null && numRows > 0){
			query.setMaxResults(numRows);
		}
		
		if(parameterQueries != null && parameterQueries.length > 0){
			for (ParametroSentencia parametroSentencia : parameterQueries) {
				query.setParameter(parametroSentencia.getNameParameter(), parametroSentencia.getValueParameter());
			}
		}
		return query.getResultList();
	}

	@Override
	public T buscarObjetosParametro(String queryAnonymous) throws UceTourException {
		return this.buscarObjetosParametro(queryAnonymous, Boolean.FALSE, new ParametroSentencia[0]);
	}

	@Override
	public T buscarObjetosParametro(String queryAnonymous, Boolean isNamed)	throws UceTourException {
		return this.buscarObjetosParametro(queryAnonymous, isNamed, new ParametroSentencia[0]);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T buscarObjetosParametro(String queryAnonymous, Boolean isNamed,	ParametroSentencia... parametroSentencias) throws UceTourException {
		Query query;
		if(isNamed){
			query = em.createNamedQuery(queryAnonymous);
		}else{
			query = em.createQuery(queryAnonymous);
		}
		
		if(parametroSentencias != null && parametroSentencias.length > 0){
			for (ParametroSentencia parametroSentencia : parametroSentencias) {
				query.setParameter(parametroSentencia.getNameParameter(), parametroSentencia.getValueParameter());
			}
		}
		query.setMaxResults(1);
		return (T) query.getResultList().get(0);
	}

}
